#!/bin/sh

# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright (C) 2012-2013 Aleksander Morgado <aleksander@gnu.org>

print_usage ()
{
    echo "usage: $0 [OPTIONS] [DEVICE] [COMMAND]"
}

help ()
{
    echo "Usage: qmi-network [OPTIONS] [DEVICE] [COMMAND]"
    echo
    echo "Simple network management of QMI devices"
    echo
    echo "Commands:"
    echo "  start           Start network connection"
    echo "  stop            Stop network connection"
    echo "  status          Query network connection status"
    echo
    echo "Options:"
    echo "  --help          Show help options"
    echo "  --version       Show version"
    echo
    echo "Notes:"
    echo
    echo "   1) [DEVICE] is given as the full path to the cdc-wdm character"
    echo "   device, e.g.:"
    echo "      /dev/cdc-wdm0"
    echo
    echo "   2) The qmi-network script requires a profile to be available"
    echo "   in the following path:"
    echo "      /etc/qmi-network.conf"
    echo
    echo "   3) The APN to use should be configured in the profile, in the"
    echo "   following way (e.g. assuming APN is called 'internet':"
    echo "      APN=internet"
    echo
    echo "   4) If you want to instruct the qmi-network script to use the"
    echo "   qmi-proxy setup, you can do so by configuring the following line"
    echo "   in the profile:"
    echo "      PROXY=yes"
    echo
    echo "   5) Once the qmi-network script reports a successful connection"
    echo "   you still need to run a DHCP client on the associated WWAN network"
    echo "   interface."
    echo
}

version ()
{
    echo "qmi-network @VERSION@"
    echo "Copyright (2013-2015) Aleksander Morgado"
    echo "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>"
    echo "This is free software: you are free to change and redistribute it."
    echo "There is NO WARRANTY, to the extent permitted by law."
    echo
}

if [ $# -ne 2 ]; then
    if [ "$1" = "--help" ]; then
        help
        exit 0
    elif [ "$1" = "--version" ]; then
        version
        exit 0
    fi

    echo "error: missing arguments" 1>&2
    print_usage
    exit 255
fi

DEVICE=$1
COMMAND=$2
STATE_FILE=/tmp/qmi-network-state
PROFILE_FILE=/etc/qmi-network.conf

load_profile ()
{
    if [ -f $PROFILE_FILE ]; then
        echo "Loading profile..."
        . $PROFILE_FILE

        if [ "x$APN" != "x" ]; then
            echo "    APN: $APN"
	    else
            echo "    APN: unset"
        fi

	    if [ "x$PROXY" = "xyes" ]; then
	        echo "    qmi-proxy: $PROXY"
	        PROXY_OPT='--device-open-proxy'
	    else
	        echo "    qmi-proxy: no"
	    fi
    else
        echo "Profile at '$PROFILE_FILE' not found..."
    fi
}

save_state ()
{
    KEY=$1
    VAL=$2

    echo "Saving state... ($KEY: $VAL)"

    if [ -f $STATE_FILE ]; then
        PREVIOUS=`cat $STATE_FILE`
        PREVIOUS=`echo "$PREVIOUS" | grep -v $KEY`
        if [ "x$PREVIOUS" != "x" ]; then
            echo $PREVIOUS > $STATE_FILE
        else
            rm $STATE_FILE
        fi
    fi

    if [ "x$VAL" != "x" ]; then
        echo "$KEY=\"$VAL\"" >> $STATE_FILE
    fi
}

load_state ()
{
    if [ -f $STATE_FILE ]; then
        echo "Loading previous state..."
        . $STATE_FILE

        if [ "x$CID" != "x" ]; then
            echo "    Previous CID: $CID"
        fi
        if [ "x$PDH" != "x" ]; then
            echo "    Previous PDH: $PDH"
        fi
    fi
}

clear_state ()
{
    echo "Clearing state..."
    rm -f $STATE_FILE
}

# qmicli -d /dev/cdc-wdm0 --wds-start-network --client-no-release-cid
# [/dev/cdc-wdm0] Network started
# 	Packet data handle: 3634026241
# [/dev/cdc-wdm0] Client ID not released:
# 	Service: 'wds'
# 	    CID: '80'
start_network ()
{
    if [ "x$CID" != "x" ]; then
        USE_PREVIOUS_CID="--client-cid=$CID"
    fi

    if [ "x$PDH" != "x" ]; then
        echo "error: cannot re-start network, PDH already exists" 1>&2
        exit 3
    fi

    # Read link layer protocol
    DATA_FORMAT_CMD="qmicli -d $DEVICE --wda-get-data-format $PROXY_OPT"
    echo "Checking data format with '$DATA_FORMAT_CMD'..."
    if [ "x$QMIDEBUG" != "x" ]; then
        DATA_FORMAT_OUT="\
[/dev/cdc-wdm1] Successfully got data format
                   QoS flow header: no
               Link layer protocol: '802-3'
  Uplink data aggregation protocol: 'disabled'
Downlink data aggregation protocol: 'disabled'
                     NDP signature: '0'
  Uplink data aggregation max size: '0'
Downlink data aggregation max size: '0'"
    else
        DATA_FORMAT_OUT=`$DATA_FORMAT_CMD`
    fi

    LLP=`echo "$DATA_FORMAT_OUT" | sed -n "s/.*Link layer protocol:.*'\(.*\)'.*/\1/p"`
    if [ "x$LLP" = "x" ]; then
        echo "Link layer protocol not retrieved" 1>&2
    else
        echo "Link layer protocol retrieved: $LLP" 1>&2
        if [ "x$LLP" != "x802-3" ]; then
            DATA_FORMAT_SET_CMD="qmicli -d $DEVICE --wda-set-data-format=802-3 $PROXY_OPT"
            echo "Updating data format with '$DATA_FORMAT_SET_CMD'..."
            if [ "x$QMIDEBUG" != "x" ]; then
                DATA_FORMAT_SET_OUT="\
[/dev/cdc-wdm1] Successfully set data format
                        QoS flow header: no
                    Link layer protocol: '802-3'
       Uplink data aggregation protocol: 'disabled'
     Downlink data aggregation protocol: 'disabled'
                          NDP signature: '0'
Downlink data aggregation max datagrams: '0'
     Downlink data aggregation max size: '0'"
            else
                DATA_FORMAT_SET_OUT=`$DATA_FORMAT_SET_CMD`
            fi

            LLP=`echo "$DATA_FORMAT_SET_OUT" | sed -n "s/.*Link layer protocol:.*'\(.*\)'.*/\1/p"`
            if [ "x$LLP" = "x" ]; then
                echo "Link layer protocol not retrieved after update" 1>&2
            else
                echo "New link layer protocol retrieved: $LLP" 1>&2
            fi
        fi
    fi

    START_NETWORK_CMD="qmicli -d $DEVICE --wds-start-network=$APN $USE_PREVIOUS_CID --client-no-release-cid $PROXY_OPT"
    echo "Starting network with '$START_NETWORK_CMD'..."

    if [ "x$QMIDEBUG" != "x" ]; then
        START_NETWORK_OUT="\
[/dev/cdc-wdm0] Network started
	Packet data handle: '3634026241'
[/dev/cdc-wdm0] Client ID not released:
	Service: 'wds'
	CID: '80'"
    else
        START_NETWORK_OUT=`$START_NETWORK_CMD`
    fi

    # Save the new CID if we didn't use any before
    if [ "x$CID" = "x" ]; then
        CID=`echo "$START_NETWORK_OUT" | sed -n "s/.*CID.*'\(.*\)'.*/\1/p"`
        if [ "x$CID" = "x" ]; then
            echo "error: network start failed, client not allocated" 1>&2
            exit 1
        else
            save_state "CID" $CID
        fi
    fi

    PDH=`echo "$START_NETWORK_OUT" | sed -n "s/.*handle.*'\(.*\)'.*/\1/p"`
    if [ "x$PDH" = "x" ]; then
        echo "error: network start failed, no packet data handle" 1>&2
        # Cleanup the client
        qmicli -d "$DEVICE" --wds-noop --client-cid="$CID" $PROXY_OPT
        clear_state
        exit 2
    else
        save_state "PDH" $PDH
    fi

    echo "Network started successfully"
}

# qmicli -d /dev/cdc-wdm0 --wds-stop-network
stop_network ()
{
    if [ "x$CID" = "x" ]; then
        echo "Network already stopped"
    elif [ "x$PDH" = "x" ]; then
        echo "Network already stopped; need to cleanup CID $CID"
        # Cleanup the client
        qmicli -d "$DEVICE" --wds-noop --client-cid="$CID" $PROXY_OPT
    else
        STOP_NETWORK_CMD="qmicli -d $DEVICE --wds-stop-network=$PDH --client-cid=$CID $PROXY_OPT"
        echo "Stopping network with '$STOP_NETWORK_CMD'..."

        if [ "x$QMIDEBUG" != "x" ]; then
            STOP_NETWORK_OUT="\
[/dev/cdc-wdm0] Network stopped
"
        else
            STOP_NETWORK_OUT=`$STOP_NETWORK_CMD`
        fi

        echo "Network stopped successfully"
    fi

    clear_state
}

# qmicli -d /dev/cdc-wdm0 --wds-get-packet-service-status
packet_service_status ()
{
    if [ "x$CID" != "x" ]; then
        USE_PREVIOUS_CID="--client-cid=$CID --client-no-release-cid"
    fi

    STATUS_CMD="qmicli -d $DEVICE --wds-get-packet-service-status $USE_PREVIOUS_CID $PROXY_OPT"
    echo "Getting status with '$STATUS_CMD'..."

    if [ "x$QMIDEBUG" != "x" ]; then
        STATUS_OUT="\
[/dev/cdc-wdm0] Connection status: 'disconnected'
"
    else
        STATUS_OUT=`$STATUS_CMD`
    fi

    CONN=`echo "$STATUS_OUT" | sed -n "s/.*Connection status:.*'\(.*\)'.*/\1/p"`
    if [ "x$CONN" = "x" ]; then
        echo "error: couldn't get packet service status" 1>&2
        exit 2
    else
        echo "Status: $CONN"
        if [ "x$CONN" != "xconnected" ]; then
            exit 64
        fi
    fi
}

# Main

# Load profile, if any
load_profile

# Load previous state, if any
load_state

# Process commands
case $COMMAND in
    "start")
        start_network
        ;;
    "stop")
        stop_network
        ;;
    "status")
        packet_service_status
        ;;
    *)
        echo "error: unexpected command '$COMMAND'" 1>&2
        print_usage
        exit 255
        ;;
esac

exit 0
